// 用c库函数实现查找*.mp3文件,windows、linux、macos等平台通用,windows下用msys2编译通过
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <dirent.h>
#include <fnmatch.h>
#include <sys/stat.h>
#include "logger.h"
#include "list-files.h"

/**
 * 分割路径为目录部分和文件部分
 * @param path 完整路径
 * @param dir_part 目录部分输出
 * @param file_part 文件部分输出
 */
void split_path(const char *path, char *dir_part, char *file_part) {
    char *last_sep = strrchr(path, PATH_SEPARATOR);

    if (last_sep) {
        size_t dir_len = last_sep - path;
        strncpy(dir_part, path, dir_len);
        dir_part[dir_len] = '\0';
        strcpy(file_part, last_sep + 1);
    } else {
        strcpy(dir_part, ".");
        strcpy(file_part, path);
    }
}

/**
 * 检查路径是否为目录
 * @param path 要检查的路径
 * @return 如果是目录返回1，否则返回0
 */
static int is_directory(const char *path) {
    struct stat path_stat;
    if (stat(path, &path_stat) != 0) {
        return 0; // 如果stat调用失败，默认不是目录
    }
    return S_ISDIR(path_stat.st_mode);
}

/**
 * 向文件列表中添加一个文件
 * @param pfiles 文件列表指针的指针
 * @param pfiles_count 文件计数指针
 * @param path 文件路径
 * @param file_name 文件名
 * @return 成功返回0，失败返回-1
 */
static int files_append(char*** pfiles, int* pfiles_count, const char *path, const char *file_name) {
    // 直接使用传入的文件计数
    int files_count = *pfiles_count;
    char **files = *pfiles;
    
    // 重新分配内存 (files_count + 2): 一个用于新文件，一个用于NULL终止符
    char **new_files = files == NULL ? 
                     malloc(sizeof(char *) * 2) : 
                     realloc(files, sizeof(char *) * (files_count + 2));
    
    if (new_files == NULL) {
        ilog("Failed to allocate memory for files\n");
        return -1;
    }
    
    files = new_files;
    *pfiles = files;
    
    // 处理NULL路径或文件名
    if (path == NULL || file_name == NULL) {
        files[files_count] = NULL; // 正确使用赋值运算符
        return 0;
    }
    
    // 为新文件路径分配内存
    files[files_count] = malloc(strlen(path) + strlen(file_name) + 2);
    if (files[files_count] == NULL) {
        ilog("Failed to allocate memory for file path\n");
        return -1;
    }
    
    // 使用snprintf构建完整路径
    int needed = snprintf(files[files_count], strlen(path) + strlen(file_name) + 2, 
        "%s%c%s", path, PATH_SEPARATOR, file_name);
    
    if (needed < 0 || needed >= (int)(strlen(path) + strlen(file_name) + 2)) {
        ilog("Failed to format file path\n");
        free(files[files_count]);
        files[files_count] = NULL;
        return -1;
    }
    
    // 更新文件计数
    *pfiles_count = files_count + 1;
    
    // 确保列表以NULL结束
    files[files_count + 1] = NULL;
    
    return 0;
}

/**
 * 根据指定模式列出文件
 * @param pfiles 文件列表指针的指针
 * @param pfiles_count 文件计数指针
 * @param search_path 搜索路径
 * @param pattern 匹配模式
 * @return 成功返回0，失败返回-1
 */
static int list_files_ptr(char*** pfiles, int* pfiles_count, const char *search_path, const char *pattern) {
    char dir_part[PATH_MAX];
    char file_part[PATH_MAX];
    struct dirent *entry;

    if (is_directory(search_path)) {
        // 如果是目录，设置目录和文件名
        strncpy(dir_part, search_path, PATH_MAX - 1);
        dir_part[PATH_MAX - 1] = '\0';
        strncpy(file_part, pattern, PATH_MAX - 1);
        file_part[PATH_MAX - 1] = '\0';
    } else {
        // 如果是文件，分割目录和文件名
        split_path(search_path, dir_part, file_part);
    }

    // 去掉目录最后的斜杠
    size_t len = strlen(dir_part);
    if (len > 0 && dir_part[len - 1] == PATH_SEPARATOR) {
        dir_part[len - 1] = '\0';
    }

    DIR *dp = opendir(dir_part);
    if (dp == NULL) {
        perror("opendir");
        return -1;
    }

    int ret = 0;
    while ((entry = readdir(dp)) && ret == 0) {
        if (fnmatch(file_part, entry->d_name, 0) == 0) {
            char *file_name = entry->d_name;
            dlog("find mp3 file: %s\n", file_name);
            int cb_ret = files_append(pfiles, pfiles_count, dir_part, file_name);
            if (cb_ret != 0) {
                ilog("error. file: %s\n", file_name);
                ret = -1;
            }
        }
    }

    closedir(dp);
    return ret;
}

/**
 * 释放文件列表内存
 * @param file_list 要释放的文件列表
 */
void free_file_list(char **file_list) {
    if (file_list == NULL) return;
    
    char **temp = file_list;
    while (*temp != NULL) {
        free(*temp);
        temp++;
    }

    free(file_list);
}

/**
 * 列出匹配指定模式的文件
 * @param search_path 搜索路径
 * @param pattern 匹配模式
 * @param count 找到的文件数量
 * @return 文件列表，使用后需调用free_file_list释放
 */
char** list_files(const char *search_path, const char *pattern, int *count) {
    // 参数验证
    if (count == NULL || search_path == NULL || pattern == NULL) {
        ilog("Invalid parameters\n");
        if (count) *count = 0;
        return NULL;
    }

    char **files = NULL;
    int files_count = 0;  // 初始化文件计数为0
    
    // 确保files初始为NULL终止
    if (files_append(&files, &files_count, NULL, NULL) != 0) {
        ilog("Failed to initialize file list\n");
        return NULL;
    }
      
    int ret = list_files_ptr(&files, &files_count, search_path, pattern);
    if (ret != 0) {
        ilog("Error listing files\n");
        free_file_list(files);
        *count = 0;
        return NULL;
    }
    
    // 直接使用计数，不需要重新计算
    *count = files_count;
    
    return files;
}
